﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed
#pragma once

#include "GcmpModelInterface.h"
#include "IObject.h"
#include "RegenDataId.h"

namespace gcmp
{
    class IDocument;
    
    /// \brief 关联更新计算器接口
    ///
    /// 计算器是关联更新的核心，每个计算器对应一个计算数据。这个计算数据称之为输出数据，由计算器负责。
    class GCMP_MODEL_INTERFACE_EXPORT ICalculator : public IObject
    {
        DEFINE_CAST_DERIVED(gcmp::ICalculator, gcmp::IObject);
    public:
        /// \brief 析构函数
        virtual ~ICalculator() {};

        /// \brief 报告计算器所需的所有数据
        ///
        /// 计算器中使用到的所有数据都在在此方法中报告出来
        /// \param regenDataIds 计算输出数据所要用到数据的RegenDataId，我们称之为输入数据
        /// \return void
        virtual void ReportInputDataIds(std::vector<RegenDataId> & regenDataIds) const = 0;

        /// \brief 计算器执行计算，根据报告的输入数据，计算输出数据
        ///
        /// 只能使用输入数据，只能修改输出数据
        /// \return void
        virtual void Execute() = 0;

        /// \brief 获取计算器名称
        /// \return 计算器名称字符串
        virtual std::wstring GetCalculatorName() const = 0;

        /// \brief 获取当前文档
        /// \return 当前文档指针
        virtual IDocument* GetDocument() const = 0;

        /// \brief 获取计算器关联的数据的Id
        /// \return 计算器关联的数据的Id
        virtual const RegenDataId& GetRegenDataId() const = 0;
    };
}

/// \brief 声明计算器的创建方法
///
/// 此宏用于声明计算器的创建方法，与IMPLEMENT_CALCULATOR_CREATOR宏配合使用
/// \param CalculatorName 计算器名称
#define DECLARE_CALCULATOR_CREATOR(CalculatorName) \
    gcmp::OwnerPtr<gcmp::ICalculator> Create##CalculatorName(gcmp::IDocument* pDoc, const RegenDataId& outputDataId);

/// \brief 实现计算器的创建方法
///
/// 此宏用于实现计算器的创建方法，与DECLARE_CALCULATOR_CREATOR宏配合使用
/// \param Namespace 命名空间
/// \param CalculatorName 计算器名称
#define IMPLEMENT_CALCULATOR_CREATOR(Namespace, CalculatorName) \
gcmp::OwnerPtr<gcmp::ICalculator> Create##CalculatorName(gcmp::IDocument* pDoc, const RegenDataId& outputDataId)     \
{                                                                                                                    \
    return TransferOwnershipCast<gcmp::ICalculator>(NEW_AS_OWNER_PTR(Namespace::CalculatorName, pDoc, outputDataId));\
}

/// \brief 添加计算器
///
/// 此宏在IElementRegenerationComponent::GetCalculators()中使用，用于添加计算器，此宏中调用了计算器的创建方法
/// \param CalculatorName 计算器名称
/// \param Document 当前文档指针IDocument*
/// \param RegenDataId 添加的计算器关联的数据的Id
#define ADD_CALCULATOR(CalculatorName, Document, RegenDataId) \
    calculators->AddCalculator(TransferOwnership(Create##CalculatorName(Document, RegenDataId)),false); 

/// \brief 添加计算器，且允许该计算器的输出结果覆盖手动设置值
///
/// 此宏在IElementRegenerationComponent::GetCalculators()中使用，用于添加计算器，此宏中调用了计算器的创建方法
/// 关联更新数据被手动设置值后，再作为计算器的输出数据被计算，默认情况下框架是不允许的。但如果通过该宏添加计算器，框架会允许该计算器对手动设置值进行覆盖。 
/// 目前该宏仅用于暂时解决机电管道弯头、BimMake门窗拷贝的场景，其他场景禁止使用。如迫不得已要使用，请先与平台进行沟通。
/// \param CalculatorName 计算器名称
/// \param Document 当前文档指针IDocument*
/// \param RegenDataId 添加的计算器关联的数据的Id
#define ADD_CALCULATOR_OVERRIDE_REGENDATA(CalculatorName, Document, RegenDataId) \
    calculators->AddCalculator(TransferOwnership(Create##CalculatorName(Document, RegenDataId)),true); 
